#include "stdafx.h"
#include "..\..\cz_def.h"
#include "..\..\function\cz_file.h"
#include "..\..\function\cz_log.h"
#include "file_def.h"
#include "VariableSizeBinFileReader.h"
#include "FileReaderMapping.h"
#include "FileReaderPool.h"
#include "FileWritterPool.h"

#define LARGE_FILE_SIZE						0x100000000			// qword

#define INNER_FLAG_NULL						0x00000000
#define INNER_FLAG_OFFSET_32				0x00000001
#define INNER_FLAG_OFFSET_64				0x00000002
#define INNER_FLAG_MAPPING					0x00000004
#define INNER_FLAG_POOL						0x00000008

#define MAX_OFFSET_SIZE						8

VariableSizeBinFileReader::VariableSizeBinFileReader()
{
	Clear();
}

VariableSizeBinFileReader::~VariableSizeBinFileReader()
{
	Uninit();
}

bool VariableSizeBinFileReader::Init(IN stringc& strBinFile, IN u32 binUseMemory, IN u32 offsetUseMemory, IN u32 readFlag)
{
	Uninit();

	m_strOffsetFile = strBinFile + ".offset";
	u32 reserveSpace;

	m_strBinFile = strBinFile;
	m_binUseMemory = binUseMemory;
	m_offsetUseMemory = offsetUseMemory;
	m_readFlag = readFlag;

	if ((m_readFlag & READ_FLAG_MAPPING) != 0) m_innerFlag |= INNER_FLAG_MAPPING;
	else if ((m_readFlag & READ_FLAG_POOL) != 0) m_innerFlag |= INNER_FLAG_POOL;
	else m_innerFlag |= INNER_FLAG_MAPPING;
	m_readFlag &= ~(READ_FLAG_MAPPING | READ_FLAG_POOL);

	if (!cz_file_IsPathExistsA(m_strOffsetFile))
	{
		if (!GenerateOffsetFile())
		{
			CZ_LOG_ERRORA("");
			goto L_Failed;
		}
	}

	if (!GetOffsetInfo())
	{
		CZ_LOG_ERRORA("");
		goto L_Failed;
	}

	if ((m_innerFlag & INNER_FLAG_MAPPING) != 0)
	{
		m_pBinFile = new FileReaderMapping;
		if (m_pBinFile == NULL)
		{
			CZ_LOG_ERRORA("");
			goto L_Failed;
		}
	}
	else if ((m_innerFlag & INNER_FLAG_POOL) != 0)
	{
		m_pBinFile = new FileReaderPool;
		if (m_pBinFile == NULL)
		{
			CZ_LOG_ERRORA("");
			goto L_Failed;
		}
	}
	
	if ((m_readFlag & READ_FLAG_FORWARD) != 0) reserveSpace = 0;
	else if (((m_readFlag & READ_FLAG_BACKWARD) != 0)) reserveSpace = m_maxRecordSize;
	else reserveSpace = 0;

	if (!m_pBinFile->Init(strBinFile, m_binUseMemory, reserveSpace, m_readFlag))
	{
		CZ_LOG_ERRORA("");
		goto L_Failed;
	}
	m_binFileSize = m_pBinFile->GetFileSize();

	return true;

L_Failed:
	Uninit();
	return false;
}

void VariableSizeBinFileReader::Uninit()
{
	if (m_pBinFile != NULL)
	{
		m_pBinFile->Uninit();
		delete m_pBinFile;
	}

	if (m_pOffsetFile != NULL)
	{
		m_pOffsetFile->Uninit();
		delete m_pOffsetFile;
	}

	if ((m_readFlag & READ_FLAG_DELETE_TEMP_FILE) != 0)
	{
		cz_file_DeletePathA(m_strOffsetFile);
	}

	Clear();
}

u32 VariableSizeBinFileReader::GetRecordNum()
{
	return m_recordNum;
}

bool VariableSizeBinFileReader::GetRecordFromIndex(IN u32 index, OUT void* pRecord)
{
	u64 offset;
	_BIN_FILE_RESULT fr;

	if (!ConvertIndexToOffset(index, offset))
	{
		CZ_LOG_ERRORA("");
		return false;
	}

	fr = GetRecordFromOffset(m_pBinFile, offset, pRecord, NULL);
	if (fr != BIN_FILE_SUCCESS)
	{
		CZ_LOG_ERRORA("");
		return false;
	}

	return true;
}

void VariableSizeBinFileReader::Clear()
{
	m_strBinFile = "";

	m_binUseMemory = 0;
	m_offsetUseMemory = 0;
	m_readFlag = READ_FLAG_NULL;

	m_pBinFile = NULL;
	m_binFileSize = 0;

	m_pOffsetFile = NULL;
	m_recordNum = 0;
	m_maxRecordSize = 0;

	m_innerFlag = INNER_FLAG_NULL;

}

bool VariableSizeBinFileReader::ConvertIndexToOffset(IN u32 index, OUT u64& offset)
{
	offset = 0;
	if (index >= m_recordNum)
	{
		CZ_LOG_ERRORA("");
		return false;
	}

	if ((m_innerFlag & INNER_FLAG_OFFSET_32) != 0)
	{
		if (!m_pOffsetFile->Getu32(index * 4, *(u32*)&offset))
		{
			CZ_LOG_ERRORA("");
			return false;
		}

		return true;
	}
	else if ((m_innerFlag & INNER_FLAG_OFFSET_64) != 0)
	{
		if (!m_pOffsetFile->Getu64(index * 8, offset))
		{
			CZ_LOG_ERRORA("");
			return false;
		}

		return true;
	}
	else
	{
		CZ_LOG_ERRORA("");
		return false;
	}

	return true;
}

bool VariableSizeBinFileReader::GenerateOffsetFile()
{
	bool r = false;
	u64 offset, binFileSize;
	u32 recordSize, offsetSize, recordNum, maxRecordSize;
	_BIN_FILE_RESULT fr;
	FileReaderMapping binFile;
	FileWritterPool offsetFile;

	if (!binFile.Init(m_strBinFile, m_offsetUseMemory, 0, READ_FLAG_FORWARD))
	{
		CZ_LOG_ERRORA("");
		goto L_End;
	}
	binFileSize = binFile.GetFileSize();

	if (!offsetFile.Init(m_strOffsetFile, 1 * 1024 * 1024))
	{
		CZ_LOG_ERRORA("");
		goto L_End;
	}
	
	offsetSize = (binFileSize < LARGE_FILE_SIZE) ? 4 : 8;

	maxRecordSize = 0;
	recordNum = 0;
	offset = 0;
	while(true)
	{
		fr = GetRecordFromOffset(&binFile, offset, NULL, &recordSize);
		if (fr == BIN_FILE_END) break;
		if (fr == BIN_FILE_ERROR)
		{
			CZ_LOG_ERRORA("");
			goto L_End;
		}

		if (maxRecordSize < recordSize) maxRecordSize = recordSize;

		if (offsetSize == 4)
		{
			if (!offsetFile.Writeu32((u32)offset))
			{
				CZ_LOG_ERRORA("");
				goto L_End;
			}
		}
		else
		{
			if (!offsetFile.Writeu64(offset))
			{
				CZ_LOG_ERRORA("");
				goto L_End;
			}
		}
		
		++recordNum;
		offset += recordSize;
	}
	if (!offsetFile.Writeu32(recordNum))
	{
		CZ_LOG_ERRORA("");
		goto L_End;
	}
	if (!offsetFile.Writeu32(offsetSize))
	{
		CZ_LOG_ERRORA("");
		goto L_End;
	}
	if (!offsetFile.Writeu32(maxRecordSize))
	{
		CZ_LOG_ERRORA("");
		goto L_End;
	}
	if (!offsetFile.ForceToWrite())
	{
		CZ_LOG_ERRORA("");
		goto L_End;
	}

	r = true;

L_End:
	binFile.Uninit();
	offsetFile.Uninit();
	if (r == false) cz_file_DeletePathA(m_strOffsetFile.c_str());
	return r;
}

bool VariableSizeBinFileReader::GetOffsetInfo()
{
	u64 fileSize;
	u32 offsetSize;

	if ((m_innerFlag & INNER_FLAG_MAPPING) != 0)
	{
		m_pOffsetFile = new FileReaderMapping;
		if (m_pOffsetFile == NULL)
		{
			CZ_LOG_ERRORA("");
			return false;
		}
	}
	else if ((m_innerFlag & INNER_FLAG_POOL) != 0)
	{
		m_pOffsetFile = new FileReaderPool;
		if (m_pOffsetFile == NULL)
		{
			CZ_LOG_ERRORA("");
			return false;
		}
	}
	
	if (!m_pOffsetFile->Init(m_strOffsetFile, m_offsetUseMemory, MAX_OFFSET_SIZE, m_readFlag))
	{
		CZ_LOG_ERRORA("");
		return false;
	}
	fileSize = m_pOffsetFile->GetFileSize();

	if (!m_pOffsetFile->Getu32(fileSize - 4, m_maxRecordSize))
	{
		CZ_LOG_ERRORA("");
		return false;
	}
	if (!m_pOffsetFile->Getu32(fileSize - 8, offsetSize))
	{
		CZ_LOG_ERRORA("");
		return false;
	}
	if (!m_pOffsetFile->Getu32(fileSize - 12, m_recordNum))
	{
		CZ_LOG_ERRORA("");
		return false;
	}

	if ((m_recordNum * offsetSize + 12) != fileSize)
	{
		CZ_LOG_ERRORA("");
		return false;
	}

	if (offsetSize == 4) m_innerFlag |= INNER_FLAG_OFFSET_32;
	else if (offsetSize == 8) m_innerFlag |= INNER_FLAG_OFFSET_64;
	else
	{
		CZ_LOG_ERRORA("");
		return false;
	}

	return true;
}